package net;

import enums.TextBundleEnum;
import enums.URLEnum;
import gui.panel_new.evaluation.professor.StudentMarksPanel;
import j_util.Utiles;
import misc.BundlesProvider;
import misc.ExportDataPreferences;
import model.eval.Test;

import javax.swing.text.JTextComponent;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Calendar;
import java.util.List;
import java.util.ResourceBundle;
/*

    This file is part of Logica-Logike.

    Logica-Logike is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation version 3 of the License.

    Logica-Logike is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Logica-Logike.  If not, see <http://www.gnu.org/licenses/>.

 */
/**
 * Corre una instancia por cliente, maneja la comunicacion entre el servidor y un cliente;
 *
 * @author Jose Martin Rozanec;
 */
public class TCPThread extends Thread {
    // the socket where to listen/talk
    private Socket socket;
    private ObjectInputStream input;
    private ObjectOutputStream output;
    private long clientID;
    private UserData data;
    private Test test;
    private List<UserData> registry;

    private JTextComponent component;
    private String baseString;
    private StudentMarksPanel studentsMarksPanel;

    private ExportDataPreferences exportPreferences;

    private ResourceBundle textBundle;

    /**
     * Constructor;
     *
     * @param socket;
     * @param test;
     * @param clientID;
     * @param registry;
     * @param studentsMarksPanel;
     */
    public TCPThread(Socket socket, Test test, long clientID, List<UserData> registry,
                     StudentMarksPanel studentsMarksPanel, ExportDataPreferences exportPreferences) {
        textBundle = BundlesProvider.getBundle(URLEnum.TEXT_BUNDLE);
        this.test = test;
        this.socket = socket;
        this.clientID = clientID;
        this.registry = registry;
        baseString = textBundle.getString(TextBundleEnum.USER.toString()) + " " + clientID + " - ";
        this.studentsMarksPanel = studentsMarksPanel;
        this.exportPreferences = exportPreferences;
    }

    /**
     * Metodo run, sigue el siguiente protocolo:
     * - mandar client ID
     * - leer userData
     * - mandar test
     * - leer clientID
     */
    public void run() {
        boolean notCheating = true;
        /* Creating both Data Stream */

        //mandar client ID
        //leer userData
        //mandar test
        //leer clientID
        setComponentText(textBundle.getString(TextBundleEnum.CREATING_INPUT_OUTPUT_STREAMS.toString()) + "...");
        try {
            // create output
            output = new ObjectOutputStream(socket.getOutputStream());
            output.flush();
            input = new ObjectInputStream(socket.getInputStream());
        } catch (IOException e) {
            setComponentText(textBundle.getString(TextBundleEnum.EXCEPTION_CREATING_INPUT_OUTPUT_STREAMS.toString()));
            return;
        }

        try {
            setComponentText(textBundle.getString(TextBundleEnum.SENDING_CLIENT_ID.toString()) + "...");
            output.writeObject(clientID);
            output.flush();
            setComponentText(textBundle.getString(TextBundleEnum.ID_SUCCESSFULY_SENT.toString()));
        } catch (IOException e) {
            setComponentText(textBundle.getString(TextBundleEnum.ERROR_WHILE_SENDING_ID.toString()));
            return;
        }
        try {
            try {
                setComponentText(textBundle.getString(TextBundleEnum.RETRIEVING_USER_DATA.toString()) + "...");
                data = (UserData) input.readObject();
                setComponentText(textBundle.getString(TextBundleEnum.USER_DATA_RETRIEVED.toString()));
                setComponentText(textBundle.getString(TextBundleEnum.IDENTIFIED_AS.toString()) + ": " +
                        data.getSurnames() + ", " + data.getNames() + "; " +
                        textBundle.getString(TextBundleEnum.STUDENT_NUMBER.toString()) + ": " + data.getStudentNumber());

                if (userPreviouslyRegistered(data)) {
                    setComponentText("\n\n" + textBundle.getString(TextBundleEnum.CHEAT_TRY.toString()) + "\n\n");
                } else {
                    registry.add(data);
                    baseString = data.getSurnames() + ", " + data.getNames() + " - ";
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            output.flush();
        } catch (IOException e) {
            setComponentText(textBundle.getString(TextBundleEnum.ERROR_WHILE_RETRIEVING_USER_DATA.toString()));
            return;
        }
        if (notCheating) {
            try {
                setComponentText(textBundle.getString(TextBundleEnum.SENDING_TEST.toString()) + "...");
                output.writeObject(test);
                output.flush();
                setComponentText(textBundle.getString(TextBundleEnum.TEST_SENT.toString()));
            } catch (IOException e) {
                System.out.println("Exception reading/writing  Streams: " + e);
                return;
            }
            try {
                setComponentText(textBundle.getString(TextBundleEnum.WAITING_TEST_ACK.toString()) + "...");
                input.readObject();
                setComponentText(textBundle.getString(TextBundleEnum.TEST_ACK_RECEIVED.toString()));
            } catch (IOException e) {
                setComponentText(textBundle.getString(TextBundleEnum.ACK_NOT_RECEIVED.toString()));
                return;
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }

            try {
                setComponentText(textBundle.getString(TextBundleEnum.WAITING_FOR_RESOLVED_TEST.toString()) + "...");
                test = (Test) input.readObject();
                setComponentText(textBundle.getString(TextBundleEnum.TEST_RECEIVED.toString()));
                setComponentText(textBundle.getString(TextBundleEnum.PROCEDING_SAVE_TEST.toString()) + "...");
                String filename = exportPreferences.getPath() +
                        data.getSurnames() + data.getNames() + "_" + Calendar.getInstance().getTimeInMillis();
                System.out.println("serialize!: "+filename);//todo delete
                Utiles.serialize(test, filename);
                this.studentsMarksPanel.addStudentMark(data.getSurnames(), data.getNames(), data.getStudentNumber(), "" + (((double)test.getAchievedScore()/test.getTotalScore())*100));//todo verificar que toma del test
                setComponentText(textBundle.getString(TextBundleEnum.TEST_SAVED.toString()));
                setComponentText(textBundle.getString(TextBundleEnum.DISCONNECTING.toString()) + "...");
            } catch (IOException e) {
                System.out.println("Exception reading/writing  Streams: " + e);
                return;
            }
            catch (ClassNotFoundException e) {
                e.printStackTrace();
            } finally {
                try {
                    output.close();
                    input.close();
                    setComponentText(textBundle.getString(TextBundleEnum.DISCONNECTED.toString()));
                }
                catch (Exception e) {
                }
            }
        }
    }

    /**
     * Permite acceder el Test;
     *
     * @return Test;
     */
    public Test getTest() {
        return test;
    }

    /**
     * Permite setear un JTextComponent sobre el cual se notificaran los estados de las
     * conexiones con los diferentes clientes;
     *
     * @param component;
     */
    public void setComponent(JTextComponent component) {
        this.component = component;
    }

    private void setComponentText(String text) {
        if (component != null) {
            if (!component.getText().equals("")) {
                component.setText(component.getText() + "\n" + baseString + text);
            } else {
                component.setText(baseString + text);
            }
        }
    }

    /**
     * Verifica si el usuario ya se halla registrado;
     *
     * @param data;
     * @return boolean;
     */
    private boolean userPreviouslyRegistered(UserData data) {
        synchronized (registry) {
            for (int j = 0; j < registry.size(); j++) {
                if (data.compareTo(registry.get(j)) == 1) {
                    return true;
                }
            }
        }
        return false;
    }
}